package com.mikelouis.test.scala.proginscala.scalacheck

/**
 * ScalaCheck有一个很有趣的特性,如果发现了导致属性测试失败的参数，在报告之前ScalaCheck会尝试最小化这个参数。
 * 这个最小化尝试在使用Prop.property和Prop.forAll方法创建属性时是自动进行的, 而在使用Prop.forAllNoShrink创建属性是不是自动进行的。
 * 下面我们来看看这些方法的区别，我们会声明一个属性——列表内没有重复元素
 *
 * Created by yuazhu on 4/21/2015.
 */
object TestCaseMinimisation {

  import org.scalacheck.Arbitrary.arbitrary
  import org.scalacheck.Prop.{forAll, forAllNoShrink}

  val p1 = forAllNoShrink(arbitrary[List[Int]])(l => l == l.distinct)

  val p2 = forAll(arbitrary[List[Int]])(l => l == l.distinct)

  val p3 = forAll((l: List[Int]) => l == l.distinct)


  // 如同可以为自定义类型定义隐式Arbitrary生成器一样, 也可以定义缺省的缩减方法。这通过定义一个返回Shrink[T]实例的隐式方法来完成。
  // 这需要使用Shrink(...)工厂方法，这个方法接受一个函数作为唯一参数并返回一个Shrink[T]实例。
  // 相应的、这个函数参数接受一个给定类型T的参数、返回一个给定值的缩减变体的流Stream。下面是ScalaCheck中为元组定义的隐式Shrink实例
  /** Shrink instance of 2-tuple
    2元组的缩减实例 最新版本源码里面用的是map shrink(t1).map((_,t2)) append shrink(t2).map((t1,_)) */
  //  import org.scalacheck.Shrink
  //  implicit def shrinkTuple2[T1, T2](implicit s1: Shrink[T1], s2: Shrink[T2]): Shrink[(T1, T2)] = Shrink {
  //    case (t1, t2) =>
  //    (for (x1 <- shrink(t1)) yield (x1, t2)) append
  //      (for (x2 <- shrink(t2)) yield (t1, x2))
  //  }

  // 实现缩减方法时要注意只能返回值的缩小的缩减版本, 否则缩减算法会(无限?)循环。
  // ScalaCheck对通用类型如整数和列表等提供了隐式的缩减方法


  def main(args: Array[String]) {
    // 在所有测试中, ScalaCheck都找到了具有重复元素的列表使得属性测试失败。不过, 后面的2个用例中列表被缩减程仅有具有两个相同元素的列表,
    // 这是指定属性的最小的失效测试用例。显然, 获得一个简单的导致失败的测试用例更容易查找bug
    println("########## p1.check ##########")
    p1.check
    println("########## p2.check ##########")
    p2.check
    println("########## p3.check ##########")
    p3.check
  }
}
